package org.turing;

import org.apache.pdfbox.pdmodel.font.PDFont;
import org.apache.pdfbox.pdmodel.font.PDType0Font;
import org.apache.poi.ss.usermodel.*;
import org.apache.poi.util.IOUtils;
import org.apache.poi.xssf.usermodel.XSSFWorkbook;
import org.apache.pdfbox.pdmodel.PDDocument;
import org.apache.pdfbox.pdmodel.PDPage;
import org.apache.pdfbox.pdmodel.PDPageContentStream;
import org.apache.pdfbox.pdmodel.common.PDRectangle;
import org.apache.pdfbox.util.Matrix;
import org.apache.pdfbox.pdmodel.graphics.state.PDExtendedGraphicsState;

import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.Date;
import java.util.List;

public class ExcelToPdfConverter {

    private static final int MAX_ROWS_PER_PAGE = 45;
    private static final float FONT_SIZE = 8;
    private static final float ROW_HEIGHT = 12;
    private static final float CELL_PADDING = 2;
    private static final float MIN_COLUMN_WIDTH = 40; // 最小列宽
    private static final float PAGE_MARGIN = 50; // 页面边距
    private static final float MAX_COLUMN_WIDTH = PDRectangle.A4.getWidth() - 2 * PAGE_MARGIN;; // 最大列宽

    public static void main(String[] args) {
        if (args.length < 2) {
            System.err.println("Usage: java -jar excel-to-pdf.jar <input-excel> <output-pdf>");
            System.err.println("Example: java -jar excel-to-pdf.jar input.xlsx output.pdf");
            System.exit(1);
        }

        String excelPath = args[0];
        String pdfPath = args[1];
        String pageTitle = args.length > 2 ? args[2] : "Excel Export";
        // 设置最大数组长度覆盖值
        IOUtils.setByteArrayMaxOverride(999999999);
        System.out.println("Starting conversion: " + excelPath + " -> " + pdfPath);

        try {
            long startTime = System.currentTimeMillis();
            convertExcelToPdf(excelPath, pdfPath, pageTitle);
            long endTime = System.currentTimeMillis();

            System.out.println("Conversion completed successfully in " + (endTime - startTime) + "ms");
            System.exit(0);
        } catch (Exception e) {
            System.err.println("Error during conversion: " + e.getMessage());
            e.printStackTrace();
            System.exit(1);
        }
    }

    public static void convertExcelToPdf(String excelPath, String pdfPath, String pageTitle) throws IOException {
        File excelFile = new File(excelPath);
        if (!excelFile.exists()) {
            throw new IOException("Excel file does not exist: " + excelPath);
        }

        try (FileInputStream file = new FileInputStream(excelFile);
             Workbook workbook = new XSSFWorkbook(file);
             PDDocument document = new PDDocument();
             InputStream fontStream = ExcelToPdfConverter.class.getClassLoader()
                     .getResourceAsStream("SIMHEI.TTF")) {
            PDFont chineseFont = PDType0Font.load(document, fontStream); // 黑体

            System.out.println("Processing workbook with " + workbook.getNumberOfSheets() + " sheets");

            // Process each sheet
            for (int sheetIndex = 0; sheetIndex < workbook.getNumberOfSheets(); sheetIndex++) {
                Sheet sheet = workbook.getSheetAt(sheetIndex);
                String sheetName = sheet.getSheetName();

                System.out.println("Processing sheet: " + sheetName + " (index: " + sheetIndex + ")");

                // Get all data from the sheet
                SheetData sheetData = extractSheetData(sheet, chineseFont);

                if (sheetData.data.isEmpty()) {
                    System.out.println("No data found in sheet: " + sheetName);
                    continue;
                }

                // Create PDF pages for this sheet
                createPdfPages(document, sheetData, sheetName, chineseFont, pageTitle);
            }

            // Save the PDF document
            document.save(pdfPath);
            System.out.println("PDF saved to: " + pdfPath);
        }
    }

    private static SheetData extractSheetData(Sheet sheet, PDFont font) throws IOException {
        List<List<String>> data = new ArrayList<>();
        List<Float> columnWidths = new ArrayList<>();
        int lastRowNum = sheet.getLastRowNum();

        // 初始化列宽列表
        int maxColumns = 0;
        Row headerRow = sheet.getRow(0);
        if (headerRow != null) {
            maxColumns = headerRow.getLastCellNum();
            for (int i = 0; i < maxColumns; i++) {
                columnWidths.add(0f);
            }
        }
        float[] widths = new float[maxColumns];
        // Process header row and calculate column widths
        if (headerRow != null) {
            List<String> header = new ArrayList<>();
            for (int cellIndex = 0; cellIndex < headerRow.getLastCellNum(); cellIndex++) {
                Cell cell = headerRow.getCell(cellIndex);
                String cellValue = getCellValueAsString(cell);
                header.add(cellValue);

                // 计算列宽
                float width = calculateTextWidth(cellValue, font) + 2 * CELL_PADDING;
                width = Math.max(MIN_COLUMN_WIDTH, Math.min(MAX_COLUMN_WIDTH, width));
                widths[cellIndex] = Math.max(width, widths[cellIndex]);
                columnWidths.set(cellIndex, Math.max(columnWidths.get(cellIndex), width));
            }
            data.add(header);
        }

        // Process data rows and update column widths
        for (int rowIndex = 1; rowIndex <= lastRowNum; rowIndex++) {
            Row row = sheet.getRow(rowIndex);
            if (row != null) {
                List<String> rowData = new ArrayList<>();
                for (int cellIndex = 0; cellIndex < row.getLastCellNum(); cellIndex++) {
                    Cell cell = row.getCell(cellIndex);
                    String cellValue = getCellValueAsString(cell);
                    rowData.add(cellValue);
                    // 更新列宽 ,只取前10行计算列宽
                    if (rowIndex < 10){
                        if (cellIndex < columnWidths.size()) {
                            float width = calculateTextWidth(cellValue, font) + 2 * CELL_PADDING;
                            width = Math.max(MIN_COLUMN_WIDTH, Math.min(MAX_COLUMN_WIDTH, width));
                            columnWidths.set(cellIndex, Math.max(columnWidths.get(cellIndex), width));
                            widths[cellIndex] = Math.max(width, widths[cellIndex]);
                        }
                    }else {
                        columnWidths.set(cellIndex, Math.max(columnWidths.get(cellIndex), widths[cellIndex]));
                    }


                }
                data.add(rowData);

                // Progress indicator for large files
                if (rowIndex % 10000 == 0) {
                    System.out.println("Processed " + rowIndex + " rows...");
                }
            }
        }

        return new SheetData(data, columnWidths);
    }

    private static void createPdfPages(PDDocument document, SheetData sheetData, String sheetName,
                                       PDFont chineseFont, String pageTitle) throws IOException {
        List<List<String>> data = sheetData.data;
        List<Float> columnWidths = sheetData.columnWidths;

        int totalRows = data.size();
        int totalColumns = columnWidths.size();

        // 计算可用页面宽度
        float pageWidth = PDRectangle.A4.getWidth() - 2 * PAGE_MARGIN;

        // 计算列分页
        List<ColumnPage> columnPages = calculateColumnPages(columnWidths, pageWidth);

        System.out.println("Creating " + columnPages.size() + " column pages for sheet: " + sheetName);

        for (int colPageIndex = 0; colPageIndex < columnPages.size(); colPageIndex++) {
            ColumnPage colPage = columnPages.get(colPageIndex);
            int totalRowPages = (int) Math.ceil((double) totalRows / MAX_ROWS_PER_PAGE);

            for (int rowPageNum = 0; rowPageNum < totalRowPages; rowPageNum++) {
                int startRow = rowPageNum * MAX_ROWS_PER_PAGE;
                int endRow = Math.min(startRow + MAX_ROWS_PER_PAGE, totalRows);

                // Get data for this page
                List<List<String>> pageData = getPageData(data, startRow, endRow, colPage.startCol, colPage.endCol);

                // Create PDF page
                PDPage page = new PDPage(PDRectangle.A4);
                document.addPage(page);
                try (PDPageContentStream contentStream = new PDPageContentStream(document, page)) {
                    // 先添加水印
                    addWatermark(contentStream, page, chineseFont);
                    // 再绘制表格
                    drawTable(contentStream, pageData, sheetName, rowPageNum + 1, totalRowPages,
                            colPageIndex + 1, columnPages.size(), chineseFont, pageTitle,
                            colPage.columnWidths, colPage.startCol);
                }
            }
        }
    }

    private static List<ColumnPage> calculateColumnPages(List<Float> columnWidths, float pageWidth) {
        List<ColumnPage> pages = new ArrayList<>();
        int startCol = 0;

        while (startCol < columnWidths.size()) {
            float currentWidth = 0;
            int endCol = startCol;

            // 计算当前页可以容纳的列
            while (endCol < columnWidths.size()) {
                float colWidth = columnWidths.get(endCol);
                if (currentWidth + colWidth > pageWidth && endCol > startCol) {
                    break;
                }
                currentWidth += colWidth;
                endCol++;
            }

            // 如果第一列就超过页面宽度，强制包含至少一列
            if (endCol == startCol) {
                endCol = startCol + 1;
            }

            List<Float> pageColumnWidths = new ArrayList<>();
            for (int i = startCol; i < endCol; i++) {
                pageColumnWidths.add(columnWidths.get(i));
            }

            pages.add(new ColumnPage(startCol, endCol - 1, pageColumnWidths));
            startCol = endCol;
        }

        return pages;
    }

    private static List<List<String>> getPageData(List<List<String>> allData, int startRow, int endRow,
                                                  int startCol, int endCol) {
        List<List<String>> pageData = new ArrayList<>();

        for (int i = startRow; i < endRow; i++) {
            List<String> originalRow = allData.get(i);
            List<String> pageRow = new ArrayList<>();

            for (int j = startCol; j <= endCol && j < originalRow.size(); j++) {
                pageRow.add(originalRow.get(j));
            }

            pageData.add(pageRow);
        }

        return pageData;
    }

    private static void addWatermark(PDPageContentStream contentStream, PDPage page, PDFont chineseFont) throws IOException {
        // 获取当前时间
        String watermarkText = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss").format(new Date());

        // 获取页面尺寸
        PDRectangle pageSize = page.getMediaBox();
        float pageWidth = pageSize.getWidth();
        float pageHeight = pageSize.getHeight();

        // 使用兼容的方法设置透明度
        PDExtendedGraphicsState graphicsState = new PDExtendedGraphicsState();
        graphicsState.setNonStrokingAlphaConstant(0.2f); // 20% 透明度
        graphicsState.setStrokingAlphaConstant(0.2f);
        contentStream.setGraphicsStateParameters(graphicsState);

        // 设置水印颜色为浅灰色
        contentStream.setNonStrokingColor(150, 150, 150); // 浅灰色

        // 设置水印字体和大小
        contentStream.setFont(chineseFont, 32); // 使用稍小的字体

        // 计算文本宽度以居中显示
        float textWidth = chineseFont.getStringWidth(watermarkText) / 1000 * 36;

        // 旋转并放置水印在页面中心
        contentStream.beginText();
        // 移动到页面中心并旋转45度
        contentStream.setTextMatrix(Matrix.getRotateInstance(Math.toRadians(45),
                (pageWidth - textWidth) / 2, pageHeight / 2));
        contentStream.showText(watermarkText);
        contentStream.endText();

        // 恢复图形状态（重置透明度和颜色）
        graphicsState.setNonStrokingAlphaConstant(1.0f);
        graphicsState.setStrokingAlphaConstant(1.0f);
        contentStream.setGraphicsStateParameters(graphicsState);
        contentStream.setNonStrokingColor(0, 0, 0); // 恢复黑色
    }

    private static void drawTable(PDPageContentStream contentStream, List<List<String>> data,
                                  String sheetName, int rowPageNum, int totalRowPages,
                                  int colPageNum, int totalColPages, PDFont font, String pageTitle,
                                  List<Float> columnWidths, int startCol) throws IOException {

        float startX = PAGE_MARGIN;
        float startY = PDRectangle.A4.getHeight() - PAGE_MARGIN;


        // 绘制页眉信息
        contentStream.setFont(font, 10);
        contentStream.beginText();
        contentStream.newLineAtOffset(startX, startY);
        contentStream.showText(pageTitle);
        contentStream.endText();

        startY -= 30;

        // 设置表格字体
        contentStream.setFont(font, FONT_SIZE);

        int rows = data.size();
        int cols = columnWidths.size();

        // 绘制表格线
        drawTableGrid(contentStream, startX, startY, rows, cols, columnWidths, ROW_HEIGHT);

        // 绘制表格内容
        for (int rowIndex = 0; rowIndex < rows; rowIndex++) {
            List<String> rowData = data.get(rowIndex);

            float yPos = startY - (rowIndex * ROW_HEIGHT) - ROW_HEIGHT + CELL_PADDING;

            for (int colIndex = 0; colIndex < Math.min(cols, rowData.size()); colIndex++) {
                String cellValue = rowData.get(colIndex);

                // 计算列起始位置
                float xPos = startX + CELL_PADDING;
                for (int i = 0; i < colIndex; i++) {
                    xPos += columnWidths.get(i);
                }

                // 截断过长的值
                float availableWidth = columnWidths.get(colIndex) - 2 * CELL_PADDING;
                cellValue = truncateTextToWidth(cellValue, font, availableWidth);

                // 绘制单元格文本
                contentStream.beginText();
                contentStream.newLineAtOffset(xPos, yPos);
                contentStream.showText(cellValue);
                contentStream.endText();
            }
        }
    }

    private static void drawTableGrid(PDPageContentStream contentStream, float startX, float startY,
                                      int rows, int cols, List<Float> columnWidths, float rowHeight) throws IOException {
        // 设置线条属性
        contentStream.setLineWidth(0.5f);
        contentStream.setStrokingColor(0, 0, 0); // 黑色

        // 计算表格总宽度
        float tableWidth = 0;
        for (float width : columnWidths) {
            tableWidth += width;
        }

        // 绘制水平线
        for (int i = 0; i <= rows; i++) {
            float y = startY - (i * rowHeight);
            contentStream.moveTo(startX, y);
            contentStream.lineTo(startX + tableWidth, y);
            contentStream.stroke();
        }

        // 绘制垂直线
        float x = startX;
        contentStream.moveTo(x, startY);
        contentStream.lineTo(x, startY - (rows * rowHeight));
        contentStream.stroke();

        for (int i = 0; i < cols; i++) {
            x += columnWidths.get(i);
            contentStream.moveTo(x, startY);
            contentStream.lineTo(x, startY - (rows * rowHeight));
            contentStream.stroke();
        }
    }

    private static float calculateTextWidth(String text, PDFont font) throws IOException {
        if (text == null || text.isEmpty()) {
            return MIN_COLUMN_WIDTH;
        }
        return font.getStringWidth(text) / 1000 * FONT_SIZE;
    }

    private static String truncateTextToWidth(String text, PDFont font, float maxWidth) throws IOException {
        if (text == null || text.isEmpty()) {
            return text;
        }

        float textWidth = calculateTextWidth(text, font);
        if (textWidth <= maxWidth) {
            return text;
        }

        // 二分查找合适的截断位置
        int left = 0;
        int right = text.length();
        int result = 0;

        while (left <= right) {
            int mid = (left + right) / 2;
            String testText = text.substring(0, mid) + "...";
            float width = calculateTextWidth(testText, font);

            if (width <= maxWidth) {
                result = mid;
                left = mid + 1;
            } else {
                right = mid - 1;
            }
        }

        if (result <= 3) {
            return "...";
        }

        return text.substring(0, result) + "...";
    }

    private static String getCellValueAsString(Cell cell) {
        if (cell == null) {
            return "";
        }

        switch (cell.getCellType()) {
            case STRING:
                return cell.getStringCellValue().trim();
            case NUMERIC:
                if (DateUtil.isCellDateFormatted(cell)) {
                    return cell.getDateCellValue().toString();
                } else {
                    // Format numeric values to avoid scientific notation
                    double num = cell.getNumericCellValue();
                    if (num == Math.floor(num)) {
                        return String.valueOf((long) num);
                    } else {
                        return String.valueOf(num);
                    }
                }
            case BOOLEAN:
                return String.valueOf(cell.getBooleanCellValue());
            case FORMULA:
                try {
                    return String.valueOf(cell.getNumericCellValue());
                } catch (Exception e) {
                    try {
                        return cell.getStringCellValue();
                    } catch (Exception e2) {
                        return cell.getCellFormula();
                    }
                }
            default:
                return "";
        }
    }

    // 内部类用于存储列分页信息
    private static class ColumnPage {
        int startCol;
        int endCol;
        List<Float> columnWidths;

        ColumnPage(int startCol, int endCol, List<Float> columnWidths) {
            this.startCol = startCol;
            this.endCol = endCol;
            this.columnWidths = columnWidths;
        }
    }

    // 内部类用于存储工作表数据和列宽信息
    private static class SheetData {
        List<List<String>> data;
        List<Float> columnWidths;

        SheetData(List<List<String>> data, List<Float> columnWidths) {
            this.data = data;
            this.columnWidths = columnWidths;
        }
    }
}